[TOOLS] Uncompress and allocate memory for gzipped kernel and initrd images on
authorIan Campbell <ian.campbell@xensource.com>
Thu, 26 Oct 2006 15:56:16 +0000 (16:56 +0100)
committerIan Campbell <ian.campbell@xensource.com>
Thu, 26 Oct 2006 15:56:16 +0000 (16:56 +0100)
the fly. We cannot rely on the length contained in the gzip trailer to determine
the length of the decompressed data because images have been observed which have
trailing junk.

Signed-off-by: Ian Campbell <ian.campbell@xensource.com>
tools/libxc/xc_linux_build.c
tools/libxc/xc_private.c
tools/libxc/xg_private.c
tools/libxc/xg_private.h

index a95ebd5c54afece9b627ec73fb8ff7c0810b9424..792d9dabae54da396304f17bf75d8e73b44b7a37 100644 (file)
 
 struct initrd_info {
     enum { INITRD_none, INITRD_file, INITRD_mem } type;
+    /*
+     * .len must be filled in by the user for type==INITRD_mem. It is
+     * filled in by load_initrd() for INITRD_file and unused for
+     * INITRD_none.
+     */
     unsigned long len;
     union {
         gzFile file_handle;
@@ -134,30 +139,42 @@ static int load_initrd(int xc_handle, domid_t dom,
                 xen_pfn_t *phys_to_mach)
 {
     char page[PAGE_SIZE];
-    unsigned long pfn_start, pfn, nr_pages;
+    unsigned long pfn_start, pfn;
 
     if ( initrd->type == INITRD_none )
         return 0;
 
     pfn_start = physbase >> PAGE_SHIFT;
-    nr_pages  = (initrd->len + PAGE_SIZE - 1) >> PAGE_SHIFT;
 
-    for ( pfn = pfn_start; pfn < (pfn_start + nr_pages); pfn++ )
+    if ( initrd->type == INITRD_mem )
     {
-        if ( initrd->type == INITRD_mem )
+        unsigned long nr_pages  = (initrd->len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+        for ( pfn = pfn_start; pfn < (pfn_start + nr_pages); pfn++ )
         {
             xc_copy_to_domain_page(
                 xc_handle, dom, phys_to_mach[pfn],
                 &initrd->u.mem_addr[(pfn - pfn_start) << PAGE_SHIFT]);
         }
-        else
+    }
+    else
+    {
+        int readlen;
+
+        pfn = pfn_start;
+        initrd->len = 0;
+
+        /* gzread returns 0 on EOF */
+        while ( (readlen = gzread(initrd->u.file_handle, page, PAGE_SIZE)) )
         {
-            if ( gzread(initrd->u.file_handle, page, PAGE_SIZE) == -1 )
+            if ( readlen < 0 )
             {
                 PERROR("Error reading initrd image, could not");
                 return -EINVAL;
             }
-            xc_copy_to_domain_page(xc_handle, dom, phys_to_mach[pfn], page);
+
+            initrd->len += readlen;
+            xc_copy_to_domain_page(xc_handle, dom, phys_to_mach[pfn++], page);
         }
     }
 
@@ -485,10 +502,17 @@ static int setup_guest(int xc_handle,
     if ( rc != 0 )
         goto error_out;
 
-    dsi.v_start      = round_pgdown(dsi.v_start);
-    vinitrd_start    = round_pgup(dsi.v_end);
-    vinitrd_end      = vinitrd_start + initrd->len;
-    v_end            = round_pgup(vinitrd_end);
+    dsi.v_start = round_pgdown(dsi.v_start);
+    (load_funcs.loadimage)(image, image_size, xc_handle, dom, page_array,
+                           &dsi);
+
+    vinitrd_start = round_pgup(dsi.v_end);
+    if ( load_initrd(xc_handle, dom, initrd,
+                     vinitrd_start - dsi.v_start, page_array) )
+        goto error_out;
+
+    vinitrd_end    = vinitrd_start + initrd->len;
+    v_end          = round_pgup(vinitrd_end);
     start_info_mpa = (nr_pages - 3) << PAGE_SHIFT;
 
     /* Build firmware.  */
@@ -525,13 +549,6 @@ static int setup_guest(int xc_handle,
            _p(dsi.v_start),     _p(v_end));
     IPRINTF(" ENTRY ADDRESS: %p\n", _p(dsi.v_kernentry));
 
-    (load_funcs.loadimage)(image, image_size, xc_handle, dom, page_array,
-                           &dsi);
-
-    if ( load_initrd(xc_handle, dom, initrd,
-                     vinitrd_start - dsi.v_start, page_array) )
-        goto error_out;
-
     *pvke = dsi.v_kernentry;
 
     /* Now need to retrieve machine pfn for system pages:
@@ -728,6 +745,24 @@ static int setup_guest(int xc_handle,
     shadow_mode_enabled = test_feature_bit(XENFEAT_auto_translated_physmap,
                                            required_features);
 
+    if ( (page_array = malloc(nr_pages * sizeof(unsigned long))) == NULL )
+    {
+        PERROR("Could not allocate memory");
+        goto error_out;
+    }
+
+    if ( xc_get_pfn_list(xc_handle, dom, page_array, nr_pages) != nr_pages )
+    {
+        PERROR("Could not get the page frame list");
+        goto error_out;
+    }
+
+    rc = (load_funcs.loadimage)(image, image_size,
+                           xc_handle, dom, page_array,
+                           &dsi);
+    if ( rc != 0 )
+        goto error_out;
+
     /*
      * Why do we need this? The number of page-table frames depends on the
      * size of the bootstrap address space. But the size of the address space
@@ -741,9 +776,14 @@ static int setup_guest(int xc_handle,
         ERROR("End of mapped kernel image too close to end of memory");
         goto error_out;
     }
+
     vinitrd_start = v_end;
+    if ( load_initrd(xc_handle, dom, initrd,
+                     vinitrd_start - dsi.v_start, page_array) )
+        goto error_out;
     if ( !increment_ulong(&v_end, round_pgup(initrd->len)) )
         goto error_out;
+
     vphysmap_start = v_end;
     if ( !increment_ulong(&v_end, round_pgup(nr_pages * sizeof(long))) )
         goto error_out;
@@ -845,28 +885,6 @@ static int setup_guest(int xc_handle,
         goto error_out;
     }
 
-    if ( (page_array = malloc(nr_pages * sizeof(unsigned long))) == NULL )
-    {
-        PERROR("Could not allocate memory");
-        goto error_out;
-    }
-
-    if ( xc_get_pfn_list(xc_handle, dom, page_array, nr_pages) != nr_pages )
-    {
-        PERROR("Could not get the page frame list");
-        goto error_out;
-    }
-
-    rc = (load_funcs.loadimage)(image, image_size,
-                           xc_handle, dom, page_array,
-                           &dsi);
-    if ( rc != 0 )
-        goto error_out;
-
-    if ( load_initrd(xc_handle, dom, initrd,
-                     vinitrd_start - dsi.v_start, page_array) )
-        goto error_out;
-
     /* setup page tables */
 #if defined(__i386__)
     if (dsi.pae_kernel != PAEKERN_no)
@@ -1350,7 +1368,6 @@ int xc_linux_build(int xc_handle,
             goto error_out;
         }
 
-        initrd_info.len = xc_get_filesz(fd);
         if ( (initrd_info.u.file_handle = gzdopen(fd, "rb")) == NULL )
         {
             PERROR("Could not allocate decompression state for initrd");
index 768cf5c5cfd32bea161057a1828a779e21668f23..a2600fdc1b6fa21b0e4e6fc5ed834361998d099f 100644 (file)
@@ -344,28 +344,6 @@ int xc_clear_domain_page(int xc_handle,
     return 0;
 }
 
-unsigned long xc_get_filesz(int fd)
-{
-    uint16_t sig;
-    uint32_t _sz = 0;
-    unsigned long sz;
-
-    lseek(fd, 0, SEEK_SET);
-    if ( read(fd, &sig, sizeof(sig)) != sizeof(sig) )
-        return 0;
-    sz = lseek(fd, 0, SEEK_END);
-    if ( sig == 0x8b1f ) /* GZIP signature? */
-    {
-        lseek(fd, -4, SEEK_END);
-        if ( read(fd, &_sz, 4) != 4 )
-            return 0;
-        sz = _sz;
-    }
-    lseek(fd, 0, SEEK_SET);
-
-    return sz;
-}
-
 void xc_map_memcpy(unsigned long dst, const char *src, unsigned long size,
                    int xch, uint32_t dom, xen_pfn_t *parray,
                    unsigned long vstart)
index 86cef6efa83d9b361912f6bd5ea1e627976a1cca..3c50552290bb07188de43f589d3b085bb7163758 100644 (file)
@@ -31,7 +31,7 @@ char *xc_read_image(const char *filename, unsigned long *size)
 {
     int kernel_fd = -1;
     gzFile kernel_gfd = NULL;
-    char *image = NULL;
+    char *image = NULL, *tmp;
     unsigned int bytes;
 
     if ( (filename == NULL) || (size == NULL) )
@@ -43,33 +43,58 @@ char *xc_read_image(const char *filename, unsigned long *size)
         goto out;
     }
 
-    if ( (*size = xc_get_filesz(kernel_fd)) == 0 )
-    {
-        PERROR("Could not read kernel image");
-        goto out;
-    }
-
     if ( (kernel_gfd = gzdopen(kernel_fd, "rb")) == NULL )
     {
         PERROR("Could not allocate decompression state for state file");
         goto out;
     }
 
-    if ( (image = malloc(*size)) == NULL )
+    *size = 0;
+
+#define CHUNK 1*1024*1024
+    while(1)
     {
-        PERROR("Could not allocate memory for kernel image");
-        goto out;
+           if ( (tmp = realloc(image, *size + CHUNK)) == NULL )
+           {
+                   PERROR("Could not allocate memory for kernel image");
+                   free(image);
+                   image = NULL;
+                   goto out;
+           }
+           image = tmp;
+
+           bytes = gzread(kernel_gfd, image + *size, CHUNK);
+           switch (bytes)
+           {
+           case -1:
+                   PERROR("Error reading kernel image");
+                   free(image);
+                   image = NULL;
+                   goto out;
+           case 0: /* EOF */
+                   goto out;
+           default:
+                   *size += bytes;
+                   break;
+           }
     }
+#undef CHUNK
 
-    if ( (bytes = gzread(kernel_gfd, image, *size)) != *size )
+ out:
+    if ( *size == 0 )
     {
-        PERROR("Error reading kernel image, could not"
-               " read the whole image (%d != %ld).", bytes, *size);
-        free(image);
-        image = NULL;
+           PERROR("Could not read kernel image");
+           free(image);
+           image = NULL;
+    }
+    else if ( image )
+    {
+           /* Shrink allocation to fit image. */
+           tmp = realloc(image, *size);
+           if ( tmp )
+                   image = tmp;
     }
 
- out:
     if ( kernel_gfd != NULL )
         gzclose(kernel_gfd);
     else if ( kernel_fd >= 0 )
index 63ad2bcfdc5c41ff52ec3c3865351fef68b47271..64ac8fad4878a2c4059656185865f5c5d8169bba 100644 (file)
@@ -193,8 +193,6 @@ typedef struct mfn_mapper {
 int xc_copy_to_domain_page(int xc_handle, uint32_t domid,
                             unsigned long dst_pfn, const char *src_page);
 
-unsigned long xc_get_filesz(int fd);
-
 void xc_map_memcpy(unsigned long dst, const char *src, unsigned long size,
                    int xch, uint32_t dom, xen_pfn_t *parray,
                    unsigned long vstart);